package DyingBaby.backend.dm.pageCache;

import DyingBaby.backend.common.AbstractCache;
import DyingBaby.backend.common.CacheConfig;
import DyingBaby.backend.common.CacheStrategy;
import DyingBaby.backend.common.InteractiveCacheConfig;
import DyingBaby.backend.dm.page.Page;
import DyingBaby.backend.dm.page.PageImpl;
import DyingBaby.utils.Panic;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import java.util.logging.Level;
import DyingBaby.common.Error;

/**
 * PageCacheImpl 是 PageCache 接口的具体实现，负责管理数据库页面的缓存和磁盘I/O。
 * 它继承自 AbstractCache<Page>，提供了页面缓存的具体实现。
 */
public class PageCacheImpl extends AbstractCache<Page> implements PageCache {
    // 缓存最小资源数，低于此限制抛出异常
    private static final int MEM_MIN_LIM = 10;
    // 数据库文件的后缀名
    public static final String DB_SUFFIX = ".db";

    private RandomAccessFile file; // 用于随机访问数据库文件的类
    private FileChannel fc; // 文件通道，用于高效的文件I/O操作
    private Lock fileLock; // 文件操作锁，确保线程安全
    // 打开文件的页面总数量，AtomicInteger保证高并发下的Integer自增自减的原子性操作
    private AtomicInteger pageNumbers; // 原子整数，记录数据库文件中的页面总数
     // 日志记录器
    private static final Logger logger = Logger.getLogger(PageCacheImpl.class.getName());

    /**
     * 构造函数
     * @param file 随机访问文件对象
     * @param fileChannel 文件通道
     * @param maxResource 最大缓存资源数（页面数）
     */
    PageCacheImpl(RandomAccessFile file, FileChannel fileChannel, int maxResource) {
       // 调用父类AbstractCache的构造方法，设置缓存的最大资源数，默认使用引用计数策略
        super(maxResource, CacheStrategy.REFERENCE_COUNT);
        initializePageCache(file, fileChannel);
    }
    
    PageCacheImpl(RandomAccessFile file, FileChannel fileChannel, int maxResource, CacheStrategy strategy) {
        // 调用父类AbstractCache的构造方法，设置缓存的最大资源数和策略
        super(maxResource, strategy);
        initializePageCache(file, fileChannel);
    }

    private void initializePageCache(RandomAccessFile file, FileChannel fileChannel) {
        // 如果缓存的最大资源数小于最小资源数，抛出异常
        if(getMaxResource() < MEM_MIN_LIM) {
            Panic.panic(Error.MemTooSmallException); // 内存太小，无法正常运行
        }
        // 获取数据库文件的长度
        long length = 0;
        try {
            length = file.length(); // 获取文件当前长度
        } catch (IOException e) {
            Panic.panic(e);
        }
        this.file = file;
        this.fc = fileChannel;
        this.fileLock = new ReentrantLock(); // 创建可重入锁
        // 计算当前文件中的页面总数（文件长度/页面大小）
        this.pageNumbers = new AtomicInteger((int)length / PAGE_SIZE);
    }

    /**
     * 创建新页面并使用初始数据初始化
     * @param initData 初始化页面的数据
     * @return 新页面的页号
     */
    public int newPage(byte[] initData) {
        // 自增加一后赋值，获取新页面的页号
        int pgno = pageNumbers.incrementAndGet();
        // 创建页面对象，初始时不脏（未修改）
        Page pg = new PageImpl(pgno, initData, null);
        // 将新页面刷写到磁盘
        flush(pg);
        return pgno;
    }

    /**
     * 根据页号从缓存中获取页面
     * @param pgno 页面编号
     * @return 请求的页面对象
     * @throws Exception 如果页面不存在或访问失败
     */
    public Page getPage(int pgno) throws Exception {
        // 只有在用户选择查看详细日志时才记录
        if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
            logger.log(Level.FINE, "请求页面: " + pgno);
        }
        Page page = get((long)pgno);
        if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
            logger.log(Level.FINE, "获取页面成功: " + pgno + ", 当前缓存统计: " + getCacheStats());
        }
        // 如果启用了自动打印且统计功能开启，则自动打印统计信息
        if (CacheConfig.isAutoPrintEnabled() && CacheConfig.isStatsEnabled()) {
            System.out.println("页面 " + pgno + " 访问完成 - " + getCacheStats());
        }
        return page;
    }

    /**
     * 当缓存未命中时，从数据库文件中读取页数据
     * @param key 页号
     * @return 从文件读取的页面对象
     * @throws Exception 读取过程中可能发生的异常
     */
    @Override
    protected Page getForCache(long key) throws Exception {
        int pgno = (int)key;
        if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
            logger.log(Level.INFO, "缓存未命中，从磁盘读取页面: " + pgno);
        }
        long offset = PageCacheImpl.pageOffset(pgno);// 计算页面偏移量

        ByteBuffer buf = ByteBuffer.allocate(PAGE_SIZE);// 分配缓冲区
        fileLock.lock();// 加锁
        try {
            fc.position(offset);// 定位页面位置
            fc.read(buf);// 读取页面数据
        } catch(IOException e) {
            if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
                logger.log(Level.SEVERE, "读取页面失败: " + pgno, e);
            }
            Panic.panic(e);
        }
        fileLock.unlock();
        if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
            logger.log(Level.INFO, "成功从磁盘读取页面: " + pgno);
        }
        return new PageImpl(pgno, buf.array(), this);// 将读取到的页数据包裹成Page并返回
    }

    // 释放页面功能
    // 脏页处理功能
    /**
     * 当当缓存已满，需要载入一个新页面时，淘汰一个旧页面，将脏页刷新到数据文件中
     * @param pg
     */
    @Override
    protected void releaseForCache(Page pg) {
        if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
            logger.log(Level.INFO, "释放页面缓存: " + pg.getPageNumber() + ", 是否脏页: " + pg.isDirty());
        }
        if(pg.isDirty()) {
            if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
                logger.log(Level.INFO, "刷新脏页到磁盘: " + pg.getPageNumber());
            }
            flush(pg);
            pg.setDirty(false);
        }
    }

    /**
     * 释放页面，减少其引用计数
     * @param page 要释放的页面
     */
    public void release(Page page) {
        release((long)page.getPageNumber());
    }

    /**
     * 将指定页面的数据刷新到磁盘
     * @param pg 要刷新的页面
     */
    public void flushPage(Page pg) {
        flush(pg);
    }

    /**
     * 内部方法，将页面数据刷写到磁盘
     * @param pg 要刷写的页面
     */
     private void flush(Page pg) {
        int pgno = pg.getPageNumber();
        // 计算页面在文件中的偏移量
        long offset = pageOffset(pgno);
        if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
            logger.log(Level.FINE, "开始刷新页面到磁盘: " + pgno + ", 偏移量: " + offset);
        }

        fileLock.lock(); // 加锁确保线程安全
        try {
            // 将页面数据包装成ByteBuffer
            ByteBuffer buf = ByteBuffer.wrap(pg.getData());
            fc.position(offset);// 定位页面位置
            fc.write(buf);// 写入页面数据
            fc.force(false);// 强制将数据写入磁盘
            if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
                logger.log(Level.FINE, "页面刷新完成: " + pgno);
            }
        } catch(IOException e) {
            if (CacheConfig.isLoggingEnabled() && InteractiveCacheConfig.getLastChoice()) {
                logger.log(Level.SEVERE, "页面刷新失败: " + pgno, e);
            }
            Panic.panic(e);
        } finally {
            fileLock.unlock(); // 确保锁被释放
        }
    }

    // 截断文件页面功能
    /**
     * 截断文件，只保留到指定页号之前的页面
     * @param maxPgno 要保留的最大页号（包括此页号）
     */
    public void truncateByBgno(int maxPgno) {
        // 计算截断后的文件大小（保留到maxPgno+1页的开始位置）
        long size = pageOffset(maxPgno + 1);
        try {
            // 设置文件长度，实现截断
            file.setLength(size);
        } catch (IOException e) {
            Panic.panic(e);
        }
        // 更新页面总数
        pageNumbers.set(maxPgno);
    }

    /**
     * 关闭页面缓存，释放所有资源
     */
    @Override
    public void close() {
        super.close(); // 调用父类的close方法，释放所有缓存页面
        try {
            fc.close(); // 关闭文件通道
            file.close(); // 关闭随机访问文件
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    /**
     * 获取当前数据库文件的页面总数
     * @return 页面数量
     */
    public int getPageNumber() {
        return pageNumbers.intValue();
    }

    /**
     * 计算指定页号在文件中的偏移量
     * @param pgno 页面编号
     * @return 页面在文件中的字节偏移量
     */
    private static long pageOffset(int pgno) {
        return (pgno-1) * PAGE_SIZE;
    }

    /**
     * 输出详细的缓存统计信息
     */
    public void printCacheStats() {
        logger.log(Level.INFO, "=== 页面缓存统计信息 ===");
        logger.log(Level.INFO, "缓存策略: " + getStrategy().getDescription());
        logger.log(Level.INFO, getCacheStats());
        logger.log(Level.INFO, "当前页面总数: " + getPageNumber());
        logger.log(Level.INFO, "========================");
    }
    
    /**
     * 重置缓存统计信息
     */
    public void resetCacheStats() {
        resetStats();
        if (CacheConfig.isLoggingEnabled()) {
            logger.log(Level.INFO, "缓存统计信息已重置");
        }
    }
    
    /**
     * 获取缓存性能报告
     */
    public String getCachePerformanceReport() {
        StringBuilder report = new StringBuilder();
        report.append("=== 页面缓存性能报告 ===\n");
        report.append("缓存策略: ").append(getStrategy().getDescription()).append("\n");
        report.append(getCacheStats()).append("\n");
        report.append("页面总数: ").append(getPageNumber()).append("\n");
        report.append("缓存利用率: ").append(String.format("%.2f%%", (double)getCurrentCount() / getMaxResource() * 100)).append("\n");
        if (getStrategy() == CacheStrategy.LRU) {
            report.append("驱逐次数: ").append(getEvictionCount()).append("\n");
        }
        report.append("========================");
        return report.toString();
    }
    
    /**
     * SQL执行后的交互式统计显示
     * 询问用户是否查看缓存统计信息
     */
    public void showInteractiveStats() {
        if (InteractiveCacheConfig.askForCacheStats()) {
            System.out.println("\n=== 缓存统计信息 ===");
            System.out.println("缓存策略: " + getStrategy().getDescription());
            System.out.println(getCacheStats());
            System.out.println("当前页面总数: " + getPageNumber());
            System.out.println("缓存利用率: " + String.format("%.2f%%", (double)getCurrentCount() / getMaxResource() * 100));
            
            if (InteractiveCacheConfig.askForDetailedLogs()) {
                System.out.println("\n=== 详细性能报告 ===");
                System.out.println(getCachePerformanceReport());
            }
            System.out.println("========================");
        }
    }
    
    /**
     * 静默模式下的统计显示（不询问用户）
     * @param showStats 是否显示统计信息
     * @param showDetailed 是否显示详细信息
     */
    public void showStats(boolean showStats, boolean showDetailed) {
        if (showStats) {
            System.out.println("\n=== 缓存统计信息 ===");
            System.out.println("缓存策略: " + getStrategy().getDescription());
            System.out.println(getCacheStats());
            System.out.println("当前页面总数: " + getPageNumber());
            System.out.println("缓存利用率: " + String.format("%.2f%%", (double)getCurrentCount() / getMaxResource() * 100));
            
            if (showDetailed) {
                System.out.println("\n=== 详细性能报告 ===");
                System.out.println(getCachePerformanceReport());
            }
            System.out.println("========================");
        }
    }
    
    /**
     * 获取缓存利用率
     * @return 缓存利用率百分比
     */
    public double getCacheUtilization() {
        return (double)getCurrentCount() / getMaxResource() * 100;
    }
    
    /**
     * 获取缓存利用率字符串
     * @return 格式化的缓存利用率字符串
     */
    public String getCacheUtilizationString() {
        return String.format("%.2f%%", getCacheUtilization());
    }
}
/*
 * 实现一个典型的页面存储管理器
 * 工作流程：
 * 页面请求 → 缓存查找 → 缓存命中？ → 是：返回缓存页面
                           ↓ 否
                        磁盘读取 → 创建页面对象 → 加入缓存 → 返回页面
 */

